package org.yfh.service;

import com.google.common.collect.Maps;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;
import org.yfh.common.EsConstant;
import org.yfh.common.exception.EsException;
import org.yfh.config.ElasticSearchConfig;
import org.yfh.service.vo.AggregateResultVO;
import org.yfh.service.vo.AttrVO;
import org.yfh.service.vo.BrandVO;
import org.yfh.service.vo.CatalogVO;

@Service
@RequiredArgsConstructor
@Slf4j
public class SkuEsAggregateService {
  private final RestHighLevelClient restHighLevelClient;

  public AggregateResultVO aggregate(String keyword) {
    SearchRequest searchRequest = new SearchRequest(EsConstant.PRODUCT_INDEX);
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    if(StringUtils.isNotEmpty(keyword)) {
      sourceBuilder.query(QueryBuilders.matchQuery("skuTitle", keyword));
    }
    sourceBuilder.aggregation(AggregationBuilders.terms("agg_brand_id").field("brandId")
        .subAggregation(AggregationBuilders.terms("agg_brand_name").field("brandName").size(1)));
    sourceBuilder.aggregation(AggregationBuilders.terms("agg_catalog_id").field("catalogId")
        .subAggregation(AggregationBuilders.terms("agg_catalog_name").field("catalogName").size(1)));

    NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("agg_attr", "attrs")
        .subAggregation(AggregationBuilders.terms("agg_attr_id").field("attrs.attrId")
            .subAggregation(AggregationBuilders.terms("agg_attr_name").field("attrs.attrName").size(1))
            .subAggregation(AggregationBuilders.terms("agg_attr_value").field("attrs.attrValue").size(50)));
    sourceBuilder.aggregation(nestedAggregationBuilder);

    log.info("构建的DSL：{}", sourceBuilder.toString()); // 可以直接复制到kibana-Dev ToolS使用
    searchRequest.source(sourceBuilder);
    try {
      SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfig
          .COMMON_OPTIONS);
      return buildAggregateResultDTO(response);
    } catch (IOException e) {
      throw new EsException("ElasticSearch检索商品异常");
    }
  }

  private AggregateResultVO buildAggregateResultDTO(SearchResponse response) {
    AggregateResultVO aggregateResultVO = new AggregateResultVO();
    List<BrandVO> brandVOS = buildBrandVOList(response);
    List<CatalogVO> catalogVOS = buildCatalogVOList(response);
    List<AttrVO> attrVOS = buildAttrVOList(response);

    aggregateResultVO.setBrands(brandVOS);
    aggregateResultVO.setCatalogs(catalogVOS);
    aggregateResultVO.setAttrs(attrVOS);
    return aggregateResultVO;
  }

  private List<BrandVO> buildBrandVOList(SearchResponse response) {
    Terms brandIdTerms = response.getAggregations().get("agg_brand_id");
    List<BrandVO> brands = Lists.newArrayList();
    for(Terms.Bucket bucket : brandIdTerms.getBuckets()) {
      Terms brandNameTerms = bucket.getAggregations().get("agg_brand_name");
      String name = String.valueOf(brandNameTerms.getBuckets().get(0).getKey());
      String id = String.valueOf(bucket.getKey());
      brands.add(new BrandVO(id, name));
    }
    return brands;
  }

  private List<CatalogVO> buildCatalogVOList(SearchResponse response) {
    Terms catalogIdTerms = response.getAggregations().get("agg_catalog_id");
    List<CatalogVO> catalogs = Lists.newArrayList();
    for(Terms.Bucket bucket : catalogIdTerms.getBuckets()) {
      Terms catalogNameTerms = bucket.getAggregations().get("agg_catalog_name");
      String name = catalogNameTerms.getBuckets().get(0).getKeyAsString();
      String id = bucket.getKeyAsString();
      catalogs.add(new CatalogVO(id, name));
    }
    return catalogs;
  }

  private List<AttrVO> buildAttrVOList(SearchResponse response) {
    Nested attrNested = response.getAggregations().get("agg_attr");
    Terms attrIdTerms = attrNested.getAggregations().get("agg_attr_id");
    List<AttrVO> result = Lists.newArrayList();
    for(Terms.Bucket attrIdBucket : attrIdTerms.getBuckets()) {
      Terms attrNameTerms = attrIdBucket.getAggregations().get("agg_attr_name");
      Terms attrValueTerms = attrIdBucket.getAggregations().get("agg_attr_value");
      String id = attrIdBucket.getKeyAsString();
      String name = attrNameTerms.getBuckets().get(0).getKeyAsString();
      List<String> attrValue = attrValueTerms.getBuckets().stream().map(Bucket::getKeyAsString).collect(
          Collectors.toList());
      result.add(new AttrVO(id, name, attrValue));
    }
    return result;
  }

}
